package com.jiunuan.live.core.server.starter;

import com.jiunuan.live.core.server.common.TcpImMsgDecoder;
import com.jiunuan.live.core.server.common.TcpImMsgEncoder;
import com.jiunuan.live.core.server.common.ChannelHandlerContextCache;
import com.jiunuan.live.core.server.handler.tcp.TcpImServerCoreHandler;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import jakarta.annotation.Resource;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;


/**
 * netty启动类
 *
 * @Author jiunuan
 * @Date: Created in 20:26 2024/8/10
 * @Description
 */
@Data
@Slf4j
@Configuration
@RefreshScope
public class TcpNettyImServerStarter implements InitializingBean {
    //指定监听的端口
    @Value("${jiunuan.im.tcp.port}")
    private int port;
    @Resource
    private TcpImServerCoreHandler tcpImServerCoreHandler;
    @Resource
    private Environment environment;
    // 基于Netty去启动一个java进程，绑定监听的端口
    public void startApplication(int port) throws InterruptedException {
        setPort(port);
        /*
           创建两个线程组bossGroup和workerGroup，
           第一个线程组处理accept事件
           第二个线程组处理read&write事件
         */
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup);
        bootstrap.channel(NioServerSocketChannel.class);
        //netty初始化相关的handler
        bootstrap.childHandler(new ChannelInitializer<>() {
            @Override
            protected void initChannel(Channel channel) throws Exception {
                // 打印日志
                log.info("[NettyImServer]初始化netty连接渠道");
                // 1. 设计消息体
                // 2. 增加编解码器
                channel.pipeline().addLast(new TcpImMsgEncoder());
                channel.pipeline().addLast(new TcpImMsgDecoder());
                channel.pipeline().addLast(tcpImServerCoreHandler);
                // 3. 设置这个netty处理handler
            }
        });
        // 基于JVM钩子函数实现优雅关闭
        Runtime.getRuntime().addShutdownHook(new Thread(()->{
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }));
        // todo 获取im的服务注册ip和暴露端口 -DUBBO_IP_TO_REGISTRY=192.168.1.159 -DUBBO_PORT_TO_REGISTRY=8076
//        String registryIp = environment.getProperty("DUBBO_IP_TO_REGISTRY");
//        String registryPort = environment.getProperty("DUBBO_PORT_TO_REGISTRY");
        String registryIp = "192.168.1.159";
        String registryPort = "13476";
        if (StringUtils.isEmpty(registryPort) || StringUtils.isEmpty(registryIp)) {
            throw new IllegalArgumentException("启动参数中的注册端口和注册ip不能为空");
        }
        ChannelHandlerContextCache.setServerIpAddress(registryIp + ":" + registryPort);
        ChannelFuture channelFuture = bootstrap.bind(port).sync();
        log.info("[NettyImServer]启动成功，监听端口：{}",port);
        // 这里会阻塞主线程，实现服务开启
        channelFuture.channel().closeFuture().sync();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 回调函数启动异步线程，启动该项目
        Thread nettyServerThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    startApplication(port);
                }catch (InterruptedException e){
                    log.error("[NettyImServer]启动失败",e);
                    throw new RuntimeException(e);
                }
            }
        });
        nettyServerThread.setName("jiunuan-live-im-server-tcp");
        nettyServerThread.start();
    }
}
